package edu.utility.Entities;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import javax.swing.JFrame;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;


import com.google.gson.Gson;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IPixelFormat;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;

import edu.utility.xuggle.MyAudio;
import edu.utility.xuggle.MyPicture;

public class Network implements Serializable{
	private Socket socket;
	private ServerSocket serverSocket;
	private Thread streamThread;
	private HashMap<String, String> map;
	private Scanner sc;
	private PrintWriter writer;
	private User user;
	private String deviceName;
	private Thread mediaThread;
	private ArrayList<String> movieTypes = new ArrayList<String>();
	private ArrayList<String> audioTypes = new ArrayList<String>();
	private ArrayList<String> pictureTypes = new ArrayList<String>();
	private String audioFile = null;
	private boolean streamAudio, streamVideo;
	int frameRate = 0;

	public Network(User user){
		this.user = user;
		checkIp();
		populateLists();
		streamAudio = false;
		streamVideo = false;
		map = new HashMap<String, String>();
		map.put("C:", "C:/");
	}	
	
	private void populateLists(){
		movieTypes.add(".avi");movieTypes.add(".mp4");
		audioTypes.add(".mp3");
		pictureTypes.add(".png");pictureTypes.add(".gif");pictureTypes.add(".jpg");
	}

	public void startStreaming(){
		streamThread = new Thread(){
			public void run() {
					while(true){
							String name = null;
							try{
								name = sc.nextLine();
							}catch(Exception ex){
								checkIp();
								connectDevice();
							}
							if(name != null){
								System.out.println(name);
								System.out.println(map.get(name));
								
								
								if(name.equals("stopAudio")){
									streamAudio = false;
								}
								else if(name.equals("stopMovie")){
									streamAudio = false;
									streamVideo = false;
								}
								else
									try{
										preformAction(new File((String)map.get(name)));
									}catch(Exception ex){}
							}
						}
					}
		};
		streamThread.start();
	}
	public void sendPicture(String fileName){
		System.out.println(fileName);
		try{
			ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
			File file = new File(map.get(fileName));
			MyPicture p = new MyPicture();
			p.setBytes(file);
			outputStream.writeObject(p);
			outputStream.flush();
		}catch(Exception ex){ex.printStackTrace();}
	}
	public void setUpMediaThread(){
		mediaThread = new Thread(new Runnable(){
			public void run() {
				try{
				streamMedia(map.get(audioFile));
				}catch(Exception ex){}
			}

		});
	}
	public void setUser(User u){
		this.user = u;
		if(user != null)
			updateUser();
	}
	private void checkIp(){
		String ip = "";
		try{
			URL whatismyip = new URL("http://steveg.dyndns-web.com:8080/Capstone/getIpAddress");
			BufferedReader in = new BufferedReader(new InputStreamReader(
					whatismyip.openStream()));

			ip = in.readLine();
		}catch(Exception ex){
			ex.printStackTrace();
		}

		if(!user.getIPAddress().equals(ip)){
			user.setIPAddress(ip);
			updateUser();
		}
	}
	public void updateUser(){
		try {
			HttpClient httpclient = new DefaultHttpClient();
			HttpPost httppost = new HttpPost("http://steveg.dyndns-web.com:8080/Capstone/updateIp");

			List<NameValuePair> nameValuePair = new ArrayList<NameValuePair>(2);
			nameValuePair.add(new BasicNameValuePair("user", new Gson().toJson(user)));
			httppost.setEntity(new UrlEncodedFormEntity(nameValuePair));
			HttpResponse response = httpclient.execute(httppost);
			user = new Gson().fromJson(EntityUtils.toString(response.getEntity()).trim(), User.class);	   
		} catch (Exception e) { }
	}

	public User getUser(){
		return user;
	}
	private void preformAction(File file) throws IOException{
		if(file.isDirectory()){
			writer.println(file.getAbsolutePath());
			map.put(file.getAbsolutePath(), file.getAbsolutePath());
			for(File f : file.listFiles(new MyFilter()))
			{
				writer.println(f.getName());
				map.put(f.getName(), f.getAbsolutePath());
			}
			writer.println("donesendingfiles");
			writer.flush();
		}
		else{
			if(isMovieType(file.getName())){
				setUpMediaThread();
				streamVideo = true;
				streamAudio = true;
				audioFile = file.getName();
				mediaThread.start();
			}
				
			else if(isAudioType(file.getName())){
				setUpMediaThread();
				streamAudio = true;
				audioFile = file.getName();
				mediaThread.start();
			}
			else if(isPictureType(file.getName()))
				sendPicture(file.getName());
		}
	}
	public boolean isMovieType(String name){
		return movieTypes.contains(name.substring(name.lastIndexOf(".")).toLowerCase());
	}
	public boolean isAudioType(String name){
		return audioTypes.contains(name.substring(name.lastIndexOf(".")).toLowerCase());
	}
	public boolean isPictureType(String name){
		return pictureTypes.contains(name.substring(name.lastIndexOf(".")).toLowerCase());
	}

	public Socket getSocket(){
		return socket;
	}

	public void connectAndroidDevice(){
		new Thread(){
			public void run(){
				connectDevice();
				startStreaming();
			}
		}.start();
	}
	private void connectDevice(){
		try{
			if(serverSocket == null){
				serverSocket = new ServerSocket(user.getPort());
			}
			System.out.println("Waiting");
			socket = serverSocket.accept();
			System.out.println("Connected");
			sc = new Scanner(socket.getInputStream());
			writer = new PrintWriter(socket.getOutputStream());
		}catch(Exception ex){}
	}

	
	public void streamMedia(String fileName) throws Exception{
		ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
		
		//Used by both video and audio.
		IContainer container = IContainer.make();
		container.open(fileName, IContainer.Type.READ, null);
		int numStreams = container.getNumStreams();
		int frameCount = 0;
		
		
		//set up audio stuff.
		int audioStreamId = -1;
		int videoStreamId = -1;
		IStreamCoder audioCoder = null;
		IStreamCoder videoCoder = null;
		IVideoResampler resampler = null;
		
		
		for(int i = 0; i < numStreams; i++)
		{
			IStream stream = container.getStream(i);
			IStreamCoder coder = stream.getStreamCoder();

			if (coder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO)
			{
				audioStreamId = i;
				audioCoder = coder;
			}
			else if(coder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO){
				videoStreamId = i;
				videoCoder = coder;
			}

		}
		audioCoder.open();
		
		if(videoCoder != null){
			videoCoder.open();
			
			
			if(videoCoder.getPixelType() != IPixelFormat.Type.BGR24 ){
				resampler = IVideoResampler.make(videoCoder.getWidth(), videoCoder.getHeight(), IPixelFormat.Type.BGR24, 
						videoCoder.getWidth(), videoCoder.getHeight(), videoCoder.getPixelType());
			}
		}
		
		IPacket packet = IPacket.make();
		
		while(container.readNextPacket(packet) >= 0 && (streamAudio || streamVideo)){
			frameRate++;
			if (packet.getStreamIndex() == audioStreamId && streamAudio){
				IAudioSamples samples = IAudioSamples.make(1024, audioCoder.getChannels());
				int offset = 0;
				while(offset < packet.getSize()){
					int bytesDecoded = audioCoder.decodeAudio(samples, packet, offset);
					offset += bytesDecoded;
					if (samples.isComplete()){
						byte[] rawBytes = samples.getData().getByteArray(0, samples.getSize());
						MyAudio audio = new MyAudio();
						audio.setBytes(rawBytes);
						try{
							outputStream.writeObject(audio);
							outputStream.flush();
						}catch(Exception ex){}
					}
				}
			}
			if(packet.getStreamIndex() == videoStreamId && streamVideo){ //get the packets from my stream
				IVideoPicture picture = IVideoPicture.make(videoCoder.getPixelType(), videoCoder.getWidth(), videoCoder.getHeight());
				int offset = 0; 
				while(offset < packet.getSize()){
					int bytesDecoded = videoCoder.decodeVideo(picture,  packet, offset);
					offset += bytesDecoded;


					//If I have the full Frame convert in into a type that works for Java
					if(picture.isComplete()){
						IVideoPicture newPict = null;
						if(picture.getPixelType() != IPixelFormat.Type.RGB4){
							newPict = IVideoPicture.make(IPixelFormat.Type.BGR24, videoCoder.getWidth(), videoCoder.getHeight());
							resampler.resample(newPict, picture);
						}else{
							newPict = picture;
						}

						//Convert my Picture to a BufferedImage and resize it.
						BufferedImage javaImage = com.xuggle.xuggler.Utils.videoPictureToImage(newPict);
						BufferedImage image = new BufferedImage(javaImage.getWidth()/4, javaImage.getHeight()/4, javaImage.getType());
						Graphics2D g = image.createGraphics();
						g.drawImage(javaImage, 0, 0, javaImage.getWidth()/4, javaImage.getHeight()/4, null);


						ByteArrayOutputStream stream = new ByteArrayOutputStream();
						ImageIO.write(image, "jpg", stream);		
						MyPicture p = new MyPicture();
						p.setBytes(stream.toByteArray());
						outputStream.writeObject(p);
						outputStream.flush();
						frameCount++;
						Thread.sleep(75);
						if(frameCount == 100){
							outputStream.reset();
							frameCount = 0;
						}

					}//end of isPictureComple
				}//end of Packet
			}//end packet is in stream.
		}
	
		if (audioCoder != null){
			audioCoder.close();
			audioCoder = null;
		}
		if(videoCoder != null){
			videoCoder.close();
			videoCoder = null;
		}
		if (container !=null){
			container.close();
			container = null;
		}
		
		System.out.println("Streaming ended");
		
		
	}
	
	public String getDeviceName() {
		return deviceName;
	}

	public void setDeviceName(String deviceName) {
		this.deviceName = deviceName;
	}

	class MyFilter implements FileFilter{
		public boolean accept(File file) {
			String type = "";
			if(file.getName().contains("."))
				type = file.getName().substring(file.getName().lastIndexOf(".")).toLowerCase();

			if(file.isHidden())
				return false;
			else if (file.isDirectory())
				return true;	
			else  if(user.isStreamAudio() && audioTypes.contains(type))
				return true;
			else if(user.isStreamMovies() && movieTypes.contains(type))
				return true;
			else if(user.isStreamPictures() && pictureTypes.contains(type))
				return true;

			return false;
		}

	}
}
